/*
 ***************************************************************************************
 * EP for web developers.Supported By Junxworks
 * @Title:  TushareService.java   
 * @Package io.github.junxworks.qt.modules.tushare.service   
 * @Description: (用一句话描述该文件做什么)   
 * @author: Administrator
 * @date:   2021年9月12日 下午4:21:51   
 * @version V1.0 
 * @Copyright: 2021 Junxworks. All rights reserved. 
 * 注意：
 *  ---------------------------------------------------------------------------------- 
 * 文件修改记录
 *     文件版本：         修改人：             修改原因：
 ***************************************************************************************
 */
package io.github.junxworks.qt.modules.tushare.service;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.beanutils.ConvertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;

import io.github.junxworks.ep.core.utils.HttpClientUtil;
import io.github.junxworks.junx.core.exception.BaseRuntimeException;
import io.github.junxworks.junx.core.util.ClockUtils.Clock;
import io.github.junxworks.junx.core.util.ObjectUtils;
import io.github.junxworks.junx.core.util.StringUtils;
import io.github.junxworks.qt.commons.DateHandleService;
import io.github.junxworks.qt.config.QtConfig;
import io.github.junxworks.qt.modules.data.dto.TushareApiRequest;
import io.github.junxworks.qt.modules.data.dto.TushareApiResponse;
import io.github.junxworks.qt.modules.data.vo.DStockProfileVo;
import net.vidageek.mirror.reflect.dsl.ReflectionHandler;
import net.vidageek.mirror.set.dsl.SetterHandler;

public class TushareService extends DateHandleService {
	private Logger log = LoggerFactory.getLogger(TushareService.class);

	public static final char UNDERLINE = '_';

	public static final String OVER_LIMITS_ERROR_MSG = "最多访问该接口";

	@Lazy
	@Autowired
	private BaseInfoService baseInfoService;

	@Autowired
	private QtConfig config;

	@SuppressWarnings("unchecked")
	protected <T> T createObj(Class<T> clazz, String[] columns, List<String> values) throws Exception {
		if (columns.length != values.size()) {
			throw new BaseRuntimeException("字段个数和值个数不匹配");
		}
		Object o = ObjectUtils.createObject(clazz);
		ReflectionHandler<T> handler = ObjectUtils.mirror().on(clazz).reflect();
		SetterHandler setter = ObjectUtils.mirror().on(o).set();
		for (int i = 0, len = columns.length; i < len; i++) {
			String fieldName = underlineToCamel(columns[i]);
			String value = values.get(i);
			Field f = handler.field(fieldName);
			if (StringUtils.isNull(value) && Float.class.equals(f.getType())) {
				setter.field(fieldName).withValue(Float.valueOf(Integer.MIN_VALUE));
			} else if (StringUtils.isNull(value) && Double.class.equals(f.getType())) {
				setter.field(fieldName).withValue(Double.valueOf(Integer.MIN_VALUE));
			} else {
				setter.field(fieldName).withValue(ConvertUtils.convert(value, f.getType()));
			}
		}
		return (T) o;
	}

	protected String extractTsCode(List<?> l) {
		if (l != null && !l.isEmpty()) {
			Object o = l.get(0);
			return String.valueOf(ObjectUtils.mirror().on(o).get().field("tsCode"));
		}
		return null;
	}

	/**
	* 驼峰格式字符串转换为下划线格式字符串
	* 
	* @param param
	* @return
	*/
	public static String camelToUnderline(String param) {
		if (param == null || "".equals(param.trim())) {
			return "";
		}
		int len = param.length();
		StringBuilder sb = new StringBuilder(len);
		for (int i = 0; i < len; i++) {
			char c = param.charAt(i);
			if (Character.isUpperCase(c)) {
				sb.append(UNDERLINE);
				sb.append(Character.toLowerCase(c));
			} else {
				sb.append(c);
			}
		}
		return sb.toString();
	}

	public static String underlineToCamel(String param) {
		if (param == null || "".equals(param.trim())) {
			return "";
		}
		StringBuilder sb = new StringBuilder(param);
		Matcher mc = Pattern.compile("_").matcher(param);
		int i = 0;
		while (mc.find()) {
			int position = mc.end() - (i++);
			sb.replace(position - 1, position + 1, sb.substring(position, position + 1).toUpperCase());
		}
		return sb.toString();
	}

	protected void intervalControl(Clock counter, long interval) {
		long cost = counter.countMillisAndReset();
		long sleepTime = interval - cost; //时间控制在1秒钟1次接口
		if (sleepTime > 0) {
			try {
				Thread.sleep(sleepTime);
			} catch (InterruptedException e) {
			}
		}
	}

	public <T> void consumeTushareDataStockByStock(String api, String fields, Class<T> returnType, Map<String, String> _params, Consumer<List<T>> consumer) {
		consumeTushareDataStockByStock(api, fields, returnType, _params, 0, consumer);
	}

	public <T> void consumeTushareDataStockByStock(String api, String fields, Class<T> returnType, Map<String, String> _params, long interval, Consumer<List<T>> consumer) {
		List<DStockProfileVo> all = baseInfoService.getAllProfiles();
		String paramName = "ts_code";
		List<String> tsCodes = all.stream().flatMap(f -> {
			return Stream.of(f.getTsCode());
		}).collect(Collectors.toList());
		consumeTushareDataOneByOne(api, fields, returnType, _params, interval, paramName, () -> {
			return tsCodes;
		}, consumer);
	}

	public <T> void consumeTushareDataOneByOne(String api, String fields, Class<T> returnType, Map<String, String> _params, long interval, String keyParamName, Supplier<List<String>> keyParamSupplier, Consumer<List<T>> consumer) {
		List<String> keys = keyParamSupplier.get();
		log.info("API[" + api + "]，总请求列表数据条数" + keys.size() + "条");
		int i = 0;
		for (String key : keys) {
			_params.put(keyParamName, key);
			consumeTushareData(api, fields, returnType, _params, consumer);
			if (++i % 100 == 0) {
				log.info("API[" + api + "]，总请求列表数据条数" + keys.size() + "条，当前已完成" + i + "条请求。");
			}
		}
		log.info("API[" + api + "]，完成" + keys.size() + "条数据请求，任务执行完毕。");
	}

	public <T> void consumeTushareData(String api, String fields, Class<T> returnType, Map<String, String> _params, Consumer<List<T>> consumer) {
		final List<T> retList = Lists.newArrayList();
		requestData(api, fields, returnType, _params, retList);
		consumer.accept(retList);
	}

	public <T> void requestData(String api, String fields, Class<T> returnType, Map<String, String> _params, List<T> retList) {
		requestData(api, fields, returnType, _params, retList, 0);
	}

	public <T> void requestData(String api, String fields, Class<T> returnType, Map<String, String> _params, List<T> retList, int triedTimes) {
		String url = config.getProfileApiURL();
		TushareApiRequest params = new TushareApiRequest();
		params.setApi_name(api);
		params.setToken(config.getProfileApiToken());
		params.setFields(fields);
		params.setParams(_params);
		if (!retList.isEmpty()) {
			params.getParams().put("offset", retList.size() + "");
		}
		String[] columns = fields.split(",");
		String data = HttpClientUtil.sendHttpPostJson(url, JSON.toJSONString(params));
		TushareApiResponse jsonObject = JSON.parseObject(data, TushareApiResponse.class);
		if ("0".equals(jsonObject.getCode())) {
			jsonObject.getData().getItems().forEach(o -> {
				try {
					retList.add(createObj(returnType, columns, o));
				} catch (Exception e) {
					log.error("解析" + api + "数据失败", e);
				}
			});
			if (jsonObject.getData().isHas_more()) {
				requestData(api, fields, returnType, _params, retList);
			}
		} else {
			String error1 = jsonObject.getMsg();
			String error2 = jsonObject.getMessage();
			log.error(error1 + "[" + error2 + "]");
			if ((StringUtils.notNull(error1) && error1.contains(OVER_LIMITS_ERROR_MSG)) || (StringUtils.notNull(error2) && error2.contains(OVER_LIMITS_ERROR_MSG))) {
				log.error("接口" + api + "频次超限额，当前重试" + triedTimes + "次，当前已请求数据" + retList.size() + "条", error1);
				try {
					Thread.sleep(config.getTushareCalmdownTime());
				} catch (InterruptedException e) {
				}
				//重试
				requestData(api, fields, returnType, _params, retList, ++triedTimes);
			} else {
				throw new BaseRuntimeException(jsonObject.getMsg() + "[" + jsonObject.getMessage() + "]");
			}
		}
	}

}
